package org.mog.gateway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Component
@Slf4j
@RequiredArgsConstructor
public class DynamicRouteLoader {
    private final NacosConfigManager nacosConfigManager;
    private final RouteDefinitionWriter routeDefinitionWriter;
    private final String dataId = "gateway-routes.json";//使用json格式，便于解析
    private final String group = "DEFAULT_GROUP";
    private Set<String> routeIds = new HashSet<>();

    @PostConstruct//在本类的这个Bean初始化之后就执行这个方法（实际上就是服务启动时）
    public void initRouteConfigListener() throws NacosException {
        log.info("服务启动，开始拉取配置");
        //服务启动先拉取一次配置，再添加配置监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;//因为简单，所以不用线程池
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //监听到配置变更，更新路由表
                        updateConfigInfo(configInfo);
                    }
                });

        //第一次拉取到配置，更新到路由表
        updateConfigInfo(configInfo);
    }

    /**
     * 更新路由表
     *
     * @param configInfo
     */
    public void updateConfigInfo(String configInfo) {
        log.info("监听到路由配置信息：{}", configInfo);
        // 解析配置文件，转换为RouteDefinition
        List<RouteDefinition> routeDefinitionList = JSONUtil.toList(configInfo, RouteDefinition.class);

        //删除旧的路由表信息
        for (String routeId : routeIds) {
            routeDefinitionWriter.delete(Mono.just(routeId));
        }
        routeIds.clear();

        //更新路由表
        for (RouteDefinition routeDefinition : routeDefinitionList) {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();//使用Mono封装routeDefinition。subscribe：订阅，这样才能生效
            //记录路由id，便于下一次更新时删除
            routeIds.add(routeDefinition.getId());
        }
    }
}
